package org.light.simpleauth;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.light.complexverb.ListMyActive;
import org.light.core.LayoutComb;
import org.light.core.ReportComb;
import org.light.core.Writeable;
import org.light.domain.Domain;
import org.light.domain.ManyToMany;
import org.light.domain.Statement;
import org.light.domain.StatementList;
import org.light.domain.Util;
import org.light.simpleauth.verb.FindUserShadow;
import org.light.simpleauth.verb.LoginUser;
import org.light.utils.DomainUtil;
import org.light.utils.StringUtil;
import org.light.utils.WriteableUtil;
import org.light.verb.FindById;
import org.light.verb.FindByName;

public class LoginServiceImplGenerator extends Util{
	protected Domain userDomain;
	protected Domain roleDomain;
	protected Domain privilegeDomain;
	protected List<Domain> domains;
	protected List<ManyToMany> mtms;
	protected List<LayoutComb> layouts;
	protected List<ReportComb> reports;
	
	public LoginServiceImplGenerator(){
		super();
		super.fileName = "login_service.rs";
	}

	@Override
	public String generateUtilString() throws Exception{
		List<Writeable> sList = new ArrayList<Writeable>();
		sList.add(new Statement(1000L,0,"#![allow(unused_imports)]"));
		sList.add(new Statement(1100L,0,"use crate::"+this.userDomain.getDomainSuffix()+"::"+this.userDomain.getCapFirstDomainNameWithSuffix()+";"));
		sList.add(new Statement(1200L,0,"use crate::{"));
		sList.add(new Statement(1400L,1,this.userDomain.getDomainSuffix()+"::error::{Error as SysError, Result},"));
		sList.add(new Statement(1500L,0,"};"));
		
		if ("Oracle".equalsIgnoreCase(this.userDomain.getDbType())) {
			sList.add(new Statement(1500L,0,"use oracle::Error;"));
		}else {
			sList.add(new Statement(1500L,0,"use sqlx::Error;"));
		}
		
		sList.add(new Statement(1900L,0,""));
		sList.add(new Statement(2000L,0,"use jsonwebtoken::DecodingKey;"));
		sList.add(new Statement(2100L,0,"use crate::utils::jwt_util::JWT_SECRET;"));
		sList.add(new Statement(2200L,0,"use jsonwebtoken::Validation;"));
		sList.add(new Statement(2300L,0,"use crate::utils::jwt_util::Claims;"));
		sList.add(new Statement(2400L,0,""));
		FindById findById = new FindById(this.userDomain);
		FindUserShadow findShadow = new FindUserShadow(this.userDomain,this.userDomain.getDbType());
		ListMyActive lsmaur = new ListMyActive(this.userDomain,this.roleDomain);
		ListMyActive lsmarp = new ListMyActive(this.roleDomain,this.privilegeDomain);
		sList.add(new Statement(2500L,0,"use crate::"+this.userDomain.getServiceimplSuffix()+"::"+this.userDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(findShadow.getVerbName())+";"));
		sList.add(new Statement(2600L,0,"use crate::"+this.userDomain.getServiceimplSuffix()+"::"+this.userDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(findById.getVerbName())+";"));
		sList.add(new Statement(2700L,0,"use crate::"+this.userDomain.getServiceimplSuffix()+"::"+this.userDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(lsmaur.getVerbName())+";"));
		sList.add(new Statement(2800L,0,"use crate::"+this.roleDomain.getServiceimplSuffix()+"::"+this.roleDomain.getSnakeDomainName()+"_service::"+StringUtil.getSnakeName(lsmarp.getVerbName())+";"));
		sList.add(new Statement(2900L,0,"use crate::"+this.roleDomain.getDomainSuffix()+"::"+this.roleDomain.getCapFirstDomainNameWithSuffix()+";"));
		sList.add(new Statement(3000L,0,"use crate::"+this.privilegeDomain.getDomainSuffix()+"::"+this.privilegeDomain.getCapFirstDomainNameWithSuffix()+";"));
		sList.add(new Statement(3100L,0,""));
		sList.add(new Statement(3200L,0,"use regex::Regex;"));
		sList.add(new Statement(3300L,0,"use crate::utils::encrypt_util::verify_password;"));
		sList.add(new Statement(9000L,0,""));
		
		sList.add(new Statement(10000L,0,"pub async fn can_access(token:String, path:String) -> bool{"));
		
		FindById find = new FindById(this.userDomain);
		ListMyActive lma = new ListMyActive(this.userDomain,this.roleDomain);
		ListMyActive lmrpa = new ListMyActive(this.roleDomain,this.privilegeDomain);
		
		sList.add(new Statement(11000L,1,"if path.contains(\"/login/\") || path.contains(\"/login"+this.userDomain.getControllerNamingSuffix()+"/\") {"));
		sList.add(new Statement(12000L,2,"return true;"));
		sList.add(new Statement(13000L,1,"}else if String::len(&token) == 0 {"));
		sList.add(new Statement(15000L,2,"return false;"));
		sList.add(new Statement(16000L,1,"}else{"));
		sList.add(new Statement(17000L,2,"let claims:Claims = jsonwebtoken::decode("));
		sList.add(new Statement(18000L,3,"&token,"));
		sList.add(new Statement(19000L,3,"&DecodingKey::from_secret(JWT_SECRET.as_bytes()),"));
		sList.add(new Statement(20000L,3,"&Validation::default(),"));
		sList.add(new Statement(21000L,2,")"));
		sList.add(new Statement(22000L,2,".map(|data| data.claims).unwrap();"));
		sList.add(new Statement(24000L,2,"let "+this.userDomain.getSnakeDomainName()+" = "+StringUtil.getSnakeName(find.getVerbName())+"(claims.sub).await.unwrap();"));
		sList.add(new Statement(26000L,2,"let "+StringUtil.getSnakeName(this.getRoleDomain().getPlural())+" = "+StringUtil.getSnakeName(lma.getVerbName())+"("+this.getUserDomain().getSnakeDomainName()+"."+this.userDomain.getDomainId().getSnakeFieldName()+").await.unwrap();"));
		sList.add(new Statement(27000L,2,"let mut "+StringUtil.getSnakeName(this.privilegeDomain.getPlural())+" = Vec::<"+this.privilegeDomain.getCapFirstDomainNameWithSuffix()+">::new();"));
		sList.add(new Statement(28000L,2,"for "+this.getRoleDomain().getSnakeDomainName()+" in &"+StringUtil.getSnakeName(this.getRoleDomain().getPlural())+" {"));
		sList.add(new Statement(30000L,3,"let mut sub_privs = "+StringUtil.getSnakeName(lmrpa.getVerbName())+"("+this.roleDomain.getSnakeDomainName()+"."+this.roleDomain.getDomainId().getSnakeFieldName()+").await.unwrap();"));
		sList.add(new Statement(31000L,3,StringUtil.getSnakeName(this.privilegeDomain.getPlural())+".append(&mut sub_privs);"));
		sList.add(new Statement(32000L,2,"}"));
		sList.add(new Statement(36000L,2,"if contains_"+this.roleDomain.getSnakeDomainName()+"("+StringUtil.getSnakeName(this.roleDomain.getPlural())+",\"admin\".to_string()) {"));
		sList.add(new Statement(37000L,3,"true"));
		sList.add(new Statement(38000L,2,"}else{"));
		sList.add(new Statement(39000L,3,"contains_all_"+this.privilegeDomain.getSnakeDomainName()+"("+StringUtil.getSnakeName(this.privilegeDomain.getPlural())+", path)"));
		sList.add(new Statement(40000L,2,"}"));
		sList.add(new Statement(41000L,1,"}"));
		sList.add(new Statement(41100L,0,"}"));
		sList.add(new Statement(41500L,0,""));

		sList.add(new Statement(42000L,0,"pub fn contains_"+this.roleDomain.getSnakeDomainName()+"("+StringUtil.getSnakeName(this.roleDomain.getPlural())+":Vec<"+this.roleDomain.getCapFirstDomainNameWithSuffix()+">,"+this.roleDomain.getDomainName().getSnakeFieldName()+":String)->bool{"));
		sList.add(new Statement(43000L,1,"let mut contains = false;"));
		sList.add(new Statement(44000L,1,"for "+this.roleDomain.getSnakeDomainName()+" in "+StringUtil.getSnakeName(this.getRoleDomain().getPlural())+" {"));
		sList.add(new Statement(45000L,2,"if "+this.roleDomain.getSnakeDomainName()+"."+this.roleDomain.getDomainName().getSnakeFieldName()+" == "+this.roleDomain.getDomainName().getSnakeFieldName()+" {"));
		sList.add(new Statement(46000L,3,"contains = true;"));
		sList.add(new Statement(47000L,2,"}"));
		sList.add(new Statement(48000L,1,"}"));
		sList.add(new Statement(49000L,1,"contains"));
		sList.add(new Statement(50000L,0,"}"));
		sList.add(new Statement(50050L,0,""));
		
		sList.add(new Statement(50100L,0,"pub fn contains_"+this.privilegeDomain.getSnakeDomainName()+"("+StringUtil.getSnakeName(this.privilegeDomain.getPlural())+":Vec<"+this.privilegeDomain.getCapFirstDomainNameWithSuffix()+">,"+this.privilegeDomain.getDomainName().getSnakeFieldName()+":String)->bool{"));
		sList.add(new Statement(50200L,1,"let mut contains = false;"));
		sList.add(new Statement(50300L,1,"for "+this.privilegeDomain.getSnakeDomainName()+" in "+StringUtil.getSnakeName(this.privilegeDomain.getPlural())+" {"));
		sList.add(new Statement(50400L,2,"if "+this.privilegeDomain.getSnakeDomainName()+"."+this.privilegeDomain.getDomainName().getSnakeFieldName()+" == "+this.privilegeDomain.getDomainName().getSnakeFieldName()+" {"));
		sList.add(new Statement(50500L,3,"contains = true;"));
		sList.add(new Statement(50600L,2,"}"));
		sList.add(new Statement(50700L,1,"}"));
		sList.add(new Statement(50800L,1,"contains"));
		sList.add(new Statement(50900L,0,"}"));
		sList.add(new Statement(50950L,0,""));
		
		sList.add(new Statement(51000L,0,"pub fn contains_all_"+this.privilegeDomain.getSnakeDomainName()+"(privs:Vec<"+this.privilegeDomain.getCapFirstDomainNameWithSuffix()+">,uri:String) -> bool {"));
		sList.add(new Statement(51100L,1,"let require_privs = get_perms_by_path(uri);"));
		sList.add(new Statement(51200L,1,"if require_privs.get(0).unwrap() == &\"annon\" || require_privs.get(0).unwrap() == &\"authc\" {"));
		sList.add(new Statement(51300L,2,"return true;"));
		sList.add(new Statement(51400L,1,"} else if require_privs.get(0).unwrap() == &\"noauth\"{"));
		sList.add(new Statement(51500L,2,"return false;"));
		sList.add(new Statement(51600L,1,"} else  if privs.len() == 0 {"));
		sList.add(new Statement(51700L,2,"return false;"));
		sList.add(new Statement(51800L,1,"} else {"));
		sList.add(new Statement(51900L,2,"for "+this.privilegeDomain.getSnakeDomainName()+" in require_privs {"));
		sList.add(new Statement(52000L,3,"if !contains_"+this.privilegeDomain.getSnakeDomainName()+"(privs.clone(),"+this.privilegeDomain.getSnakeDomainName()+") {"));
		sList.add(new Statement(52100L,4,"return false;"));
		sList.add(new Statement(52200L,3,"}"));
		sList.add(new Statement(52300L,2,"}"));
		sList.add(new Statement(52400L,2,"true"));
		sList.add(new Statement(52500L,1,"}"));
		sList.add(new Statement(52600L,0,"}"));
		sList.add(new Statement(52700L,0,""));

		sList.add(new Statement(53000L,0,"fn get_perms_by_path(path:String) -> Vec<String>{"));		
		sList.add(new Statement(53500L,1,"if path == \"/\".to_string() {"));
		sList.add(new Statement(54000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(55000L,1,"}"));
		sList.add(new Statement(56000L,1,"if path == \"/favicon.ico\".to_string() {"));
		sList.add(new Statement(57000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(58000L,1,"}"));
		sList.add(new Statement(59000L,1,"if path == \"/pages/\".to_string() {"));
		sList.add(new Statement(60000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(61000L,1,"}"));
		sList.add(new Statement(62000L,1,"if path == \"/index.html\".to_string() {"));
		sList.add(new Statement(63000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(64000L,1,"}"));
		sList.add(new Statement(65000L,0,""));
		sList.add(new Statement(66000L,1,"if path.starts_with(\"/images/\"){"));
		sList.add(new Statement(67000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(68000L,1,"}"));
		sList.add(new Statement(69000L,0,""));
		sList.add(new Statement(70000L,1,"if path.starts_with(\"/js/\") {"));
		sList.add(new Statement(71000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(72000L,1,"}"));
		sList.add(new Statement(73000L,0,""));
		sList.add(new Statement(74000L,1,"if path.starts_with(\"/easyui/\") {"));
		sList.add(new Statement(75000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(76000L,1,"}"));
		sList.add(new Statement(77000L,0,""));
		sList.add(new Statement(78000L,1,"if path.starts_with(\"/echarts/\"){"));
		sList.add(new Statement(79000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(80000L,1,"}"));
		sList.add(new Statement(81000L,0,""));
		sList.add(new Statement(82000L,1,"if path.starts_with(\"/uploadjs/\"){"));
		sList.add(new Statement(83000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(84000L,1,"}"));
		sList.add(new Statement(85000L,0,""));
		sList.add(new Statement(86000L,1,"if path == \"/login/index.html\" {"));
		sList.add(new Statement(87000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(88000L,1,"}"));
		sList.add(new Statement(89000L,0,""));
		sList.add(new Statement(90000L,1,"if path == \"/login/register.html\" {"));
		sList.add(new Statement(91000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(92000L,1,"}"));
		sList.add(new Statement(93000L,0,""));
		sList.add(new Statement(94000L,1,"if  path == \"/login/error.html\" {"));
		sList.add(new Statement(95000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(96000L,1,"}"));
		sList.add(new Statement(97000L,0,""));
		sList.add(new Statement(98000L,1,"if  path == \"/login/noauth.html\" {"));
		sList.add(new Statement(99000L,2,"return  vec![\"annon\".to_string()];"));
		sList.add(new Statement(100000L,1,"}"));
		sList.add(new Statement(101000L,0,""));
		sList.add(new Statement(102000L,1,"if  path == \"/pages/index.html\"{"));
		sList.add(new Statement(103000L,2,"return  vec![\"authc\".to_string()];"));
		sList.add(new Statement(104000L,1,"}"));
				
		sList.add(new Statement(105000L,0,""));
		sList.add(new Statement(107000L,1,"if path.starts_with(\"/login"+this.userDomain.getControllerNamingSuffix()+"/\") {"));
		sList.add(new Statement(108000L,2,"return  vec![\"authc\".to_string()];"));
		sList.add(new Statement(109000L,1,"}"));
		sList.add(new Statement(110000L,0,""));
		
		sList.add(new Statement(110100L,1,"if path.starts_with(\"/profile"+this.userDomain.getControllerNamingSuffix()+"/\") {"));
		sList.add(new Statement(110200L,2,"return  vec![\"authc\".to_string()];"));
		sList.add(new Statement(110300L,1,"}"));
		sList.add(new Statement(110400L,0,""));
		
		sList.add(new Statement(111000L,1,"let re = Regex::new(r\"/*"+this.userDomain.getControllerNamingSuffix()+"/find*\").unwrap();"));
		sList.add(new Statement(112000L,1,"if re.is_match(&path){"));
		sList.add(new Statement(113000L,2,"return  vec![\"authc\".to_string()];"));
		sList.add(new Statement(114000L,1,"}"));
		sList.add(new Statement(115000L,0,""));
		sList.add(new Statement(116000L,1,"let re2 = Regex::new(r\"/*"+this.userDomain.getControllerNamingSuffix()+"/listActive*\").unwrap();"));
		sList.add(new Statement(117000L,1,"if re2.is_match(&path){"));
		sList.add(new Statement(118000L,2,"return  vec![\"authc\".to_string()];"));
		sList.add(new Statement(119000L,1,"}"));
		sList.add(new Statement(120000L,0,""));
		sList.add(new Statement(121000L,1,"let re3 = Regex::new(r\"/*"+this.userDomain.getControllerNamingSuffix()+"/listAll*\").unwrap();"));
		sList.add(new Statement(122000L,1,"if re3.is_match(&path){"));
		sList.add(new Statement(123000L,2,"return  vec![\"authc\".to_string()];"));
		sList.add(new Statement(124000L,1,"}"));
		sList.add(new Statement(125000L,1,""));
		
		long serial = 126000L;
		Set<Domain> adminDomains = new TreeSet<>();
		adminDomains.add(this.getUserDomain());
		adminDomains.add(this.getRoleDomain());
		adminDomains.add(this.getPrivilegeDomain());
		if (this.getDomains()!=null&&this.getDomains().size()>0) {
			Set<Domain> myDomains = new TreeSet<>();
			myDomains.addAll(this.getDomains());
			for (Domain d:myDomains) {
				if (!DomainUtil.inDomainSet(d,adminDomains)) {
					sList.add(new Statement(serial,1,"if path == \"/pages/"+d.getPlural().toLowerCase()+".html\" {"));
					sList.add(new Statement(serial+1000L,2,"return  vec![\""+d.getStandardName()+"\".to_string()];"));
					sList.add(new Statement(serial+2000L,1,"}"));
					sList.add(new Statement(serial+2500L,1,""));
					sList.add(new Statement(serial+3000L,1,"if path.starts_with(\"/"+d.getLowerFirstDomainName()+d.getControllerNamingSuffix()+"/\") {"));
					sList.add(new Statement(serial+4000L,2,"return  vec![\""+d.getStandardName()+"\".to_string()];"));
					sList.add(new Statement(serial+5000L,1,"}"));
					sList.add(new Statement(serial+6000L,1,""));
					serial += 7000L;
				}
			}
		}
		
		if (this.getMtms()!=null&&this.getMtms().size()>0) {
			for (ManyToMany mtm:this.getMtms()) {
				if (!DomainUtil.inDomainSet(mtm.getMaster(), adminDomains)) {
					sList.add(new Statement(serial,1,"if path == \"/pages/"+mtm.getStandardName().toLowerCase()+".html\" {"));
					sList.add(new Statement(serial+1000L,2,"return  vec![\""+mtm.getMaster().getStandardName()+"\".to_string()];"));
					sList.add(new Statement(serial+2000L,1,"}"));
				}
				serial += 3000L;
			}
		}
		if (this.getLayouts()!=null&&this.getLayouts().size()>0) {
			for (LayoutComb lc:this.getLayouts()) {
				Set<Domain> mydomains = lc.getDomains();
				mydomains.removeAll(adminDomains);
				sList.add(new Statement(serial,1,"if path == \"/pages/"+lc.getStandardName().toLowerCase()+".html\" {"));
				sList.add(new Statement(serial+1000L,2,"return  vec!["+DomainUtil.DomainSetNamesWithQuoteAndToString(mydomains)+"];"));
				sList.add(new Statement(serial+2000L,1,"}"));
				sList.add(new Statement(serial+3000L,1,""));
				serial += 4000L;
			}
		}
		if (this.getReports()!=null&&this.getReports().size()>0) {
			for (ReportComb rc:this.getReports()) {
				Set<Domain> mydomains = rc.getDomains();
				mydomains.removeAll(adminDomains);
				sList.add(new Statement(serial,1,"if path == \"/pages/"+rc.getStandardName().toLowerCase()+".html\" {"));
				sList.add(new Statement(serial+1000L,2,"return  vec!["+DomainUtil.DomainSetNamesWithQuoteAndToString(mydomains)+"];"));
				sList.add(new Statement(serial+2000L,1,"}"));
				sList.add(new Statement(serial+3000L,1,""));
				serial += 4000L;
			}
		}

		sList.add(new Statement(serial+19000L,1,"return  vec![\"noauth\".to_string()];"));
		sList.add(new Statement(serial+20000L,0,"}"));
		sList.add(new Statement(serial+21000L,0,""));
		LoginUser login = new LoginUser(this.userDomain);
		StatementList sl = login.generateServiceImplMethod().generateMethodFullStatements();
		sl.setSerial(serial+22000L);
		sList.add(sl);
		return WriteableUtil.merge(sList).getContent();		
	}

	public Domain getUserDomain() {
		return userDomain;
	}

	public void setUserDomain(Domain userDomain) {
		this.userDomain = userDomain;
	}

	public Domain getRoleDomain() {
		return roleDomain;
	}

	public void setRoleDomain(Domain roleDomain) {
		this.roleDomain = roleDomain;
	}

	public Domain getPrivilegeDomain() {
		return privilegeDomain;
	}

	public void setPrivilegeDomain(Domain privilegeDomain) {
		this.privilegeDomain = privilegeDomain;
	}

	public List<Domain> getDomains() {
		return domains;
	}

	public void setDomains(List<Domain> domains) {
		this.domains = domains;
	}

	public List<ManyToMany> getMtms() {
		return mtms;
	}

	public void setMtms(List<ManyToMany> mtms) {
		this.mtms = mtms;
	}

	public List<LayoutComb> getLayouts() {
		return layouts;
	}

	public void setLayouts(List<LayoutComb> layouts) {
		this.layouts = layouts;
	}

	public List<ReportComb> getReports() {
		return reports;
	}

	public void setReports(List<ReportComb> reports) {
		this.reports = reports;
	}

}
